{*******************************************************************************
* The contents of this file are used with permission, subject to the Mozilla   *
* Public License Version 1.1 (the "License"); you may not use this file except *
* in compliance with the License. You may obtain a copy of the License at      *
* http://www.mozilla.org/MPL/                                                  *
*                                                                              *
* Software distributed under the License is distributed on an "AS IS" basis,   *
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for *
* the specific language governing rights and limitations under the License.    *
*                                                                              *
* The Original Code is distributed as part of the "mediate" product and is     *
* Copyright (C) @home media limited. All Rights Reserved.                      *
*                                                                              *
* Email: support@athomemedia.co.uk                                             *
* Web:   http://www.athomemedia.co.uk                                          *
*******************************************************************************}

unit DVD;

interface

uses Types, AHMTypes, AVRenderer, Canvas, Colour;

const
  VOLUME_SCALE = 100; // Scale factor to convert 0..-4000 DirectShow range to our 0..-40 range
  POSITION_SEEK_DELAY = 500; // Minimum seek delay period in milliseconds

type
  TAHMDVDRenderer = class(TAHMAVRenderer)
  protected
    function GetDuration: LongInt; override;
    function GetPosition: LongInt; override;
    function GetChapterCount: Integer; override;
    procedure SetPosition(Position: LongInt); override;
    procedure SetVolume(Volume: Integer); override;
  public
    constructor Create;
    procedure UpdateMediaState; override;
    procedure UpdateRendererSettings; override;
    procedure Render(Rect: TAHMRectF; Shade: TAHMCanvasShade; Gamma: TAHMCanvasColour;
                     XScale, YScale: Single; XFlip, YFlip: Boolean); override;
    function Play: Boolean; override;
    function Pause: Boolean; override;
    function Stop: Boolean; override;
    function Mute: Boolean; override;
    function UnMute: Boolean; override;
    function SkipForward: Boolean; override;
    function SkipBackward: Boolean; override;
    function FastForward: Boolean; override;
    function Rewind: Boolean; override;
    function NextChapter: Boolean; override;
    function PreviousChapter: Boolean; override;
    function MenuActive: Boolean; override;
    function MenuReturn: Boolean; override;
    function MenuExit: Boolean; override;
    function MenuSelect: Boolean; override;
    function MenuSelect(X, Y: Single): Boolean; override;
    function MenuNavigate(Direction: TAHMDirection): Boolean; override;
    function MenuNavigate(X, Y: Single): Boolean; override;
  end;

implementation

uses ApplicationManager, Actions, DirectXHelper, DirectShowManager, Timing;

constructor TAHMDVDRenderer.Create;
begin
  inherited;

  // Set position seeking interval for audio source
  FPositionInterval := POSITION_SEEK_DELAY;
end;

function TAHMDVDRenderer.GetDuration: LongInt;
begin
  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.Duration
    else
      Result := 0;
end;

function TAHMDVDRenderer.GetPosition: LongInt;
begin
  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.Position
    else
      Result := 0;
end;

function TAHMDVDRenderer.GetChapterCount: Integer;
begin
  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.ChapterCount
    else
      Result := 0;
end;

procedure TAHMDVDRenderer.SetPosition(Position: LongInt);
begin
  // Inform ui timer that we're about to start rendering video
  TimerInstance.PreRequestRender;

  // Okay to update media position?
  if OkayToPositionMedia then
    with DirectShowInstance do
      if Assigned(DirectShowDVDRenderer) then
        DirectShowDVDRenderer.Seek(Position);
end;

procedure TAHMDVDRenderer.SetVolume(Volume: Integer);
begin
  inherited SetVolume(Volume);

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      DirectShowDVDRenderer.InternalVolume := Volume * VOLUME_SCALE;
end;

procedure TAHMDVDRenderer.UpdateMediaState;
begin
  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
    begin
      // Check for any outstanding direct show events
      DirectShowDVDRenderer.CheckEventQueue;

      // Only really interested in end of file event - signal next item
      if DirectShowDVDRenderer.EndOfFile then SignalMediaEvent(msigNext);
    end;
end;

procedure TAHMDVDRenderer.UpdateRendererSettings;
begin
  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
    begin
      DirectShowDVDRenderer.UpdateAudioDSPSettings;
    end;
end;

procedure TAHMDVDRenderer.Render(Rect: TAHMRectF; Shade: TAHMCanvasShade;
                                 Gamma: TAHMCanvasColour; XScale, YScale: Single;
                                 XFlip, YFlip: Boolean);
begin
  inherited;

  if MediaState = msStopped then Exit;

  // Determine whether we're running dvd fullscreen
  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then DirectShowDVDRenderer.Fullscreen := FFullscreen;

  // Delegate this task to DirectX layer
  DirectXInstance.DrawDVDFrame(Rect, BackgroundColour, Shade, Gamma, XScale, YScale, XFlip, YFlip);
end;

function TAHMDVDRenderer.Play: Boolean;
begin
  // Set default result - couldn't play
  Result := False;

  if Assigned(Playlist.CurrentItem) then
  begin
    with DirectShowInstance do
    begin
      // Check if video renderer is currently in use
      if Assigned(DirectShowDVDRenderer) then
      begin
        // In use - if we own it then just resume playback
        if DirectShowDVDRenderer.Owner = Self then
          DirectShowDVDRenderer.Play
        else
        begin
          // Owned by other stream, so nick it, but inform other stream first
          TAHMAVRenderer(DirectShowDVDRenderer.Owner).SignalMediaEvent(msigAbort);
          StopDVD;
        end;
      end;

      // Check if video renderer needs initialising
      if not Assigned(DirectShowDVDRenderer) then
      begin
//        if not PlayVideo(Playlist.CurrentItem.URL) then
        if not PlayDVD then
        begin
          // Map DirectShow video error into one of our generic error types
          case DirectShowInstance.LastVideoError of
            dvOkay:         LastError := meNone;
            dvNoDirectShow: LastError := meInstallationBroken;
            dvNoVMR:        LastError := meUnsupportedHardware;
            dvMissingCodec: LastError := meUnsupportedMedia;
            dvBrokenCodec:  LastError := meExternalError;
          end;
          Exit;
        end;

        DirectShowDVDRenderer.InternalVolume := Volume * VOLUME_SCALE;
        if Muted then DirectShowDVDRenderer.InternalFastMute;
        DirectShowDVDRenderer.Owner := Self;
      end;

      // Inform ui timer that we're about to start rendering video
      TimerInstance.PreRequestRender;

      Result := True;
    end;
  end;
end;

function TAHMDVDRenderer.Pause: Boolean;
begin
  // Set default result - couldn't pause
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
    begin
      DirectShowDVDRenderer.Pause;

      // Resume fullspeed UI rendering
      TimerInstance.CancelRenderRequests;

      Result := True;
    end;
end;

function TAHMDVDRenderer.Stop: Boolean;
begin
  // Stop video stream
  DirectShowInstance.StopDVD;

  // Resume fullspeed UI rendering
  TimerInstance.CancelRenderRequests;

  Result := True;
end;

function TAHMDVDRenderer.Mute: Boolean;
begin
  // Set default result - couldn't mute
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.InternalFastMute;
end;

function TAHMDVDRenderer.UnMute: Boolean;
begin
  // Set default result - couldn't unmute
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.InternalFastUnMute;
end;

function TAHMDVDRenderer.SkipForward: Boolean;
begin
  // Stop current file & play new item
  Stop;
  Result := Play;
end;

function TAHMDVDRenderer.SkipBackward: Boolean;
begin
  // Stop current file & play new item
  Stop;
  Result := Play;
end;

function TAHMDVDRenderer.FastForward: Boolean;
var
  Position: LongInt;
begin
  // Set default result - can't fast forward
  Result := False;

  // Inform ui timer that we're about to start rendering video
  TimerInstance.PreRequestRender;

  // Okay to update media position?
  if not OkayToPositionMedia then
  begin
    // We recently updated media position, so give DirectShow a chance to
    // catch up before we make another request. However, we'll return a true
    // result to prevent media manager from delegating the request to the
    // other media stream.
    Result := True;
  end
  else
    with DirectShowInstance do
      if Assigned(DirectShowDVDRenderer) then
      begin
        // Try to skip chapters before we fail over to fast forwarding
        if NextChapter then
          Result := True
        else
        begin
          Position := DirectShowDVDRenderer.Position;
          Position := Position + DEFAULT_FWD_SKIP;
          if Position > DirectShowDVDRenderer.Duration then
            Position := DirectShowDVDRenderer.Duration;
          Result := DirectShowDVDRenderer.Seek(Position);
        end;
      end;
end;

function TAHMDVDRenderer.Rewind: Boolean;
var
  Position: LongInt;
begin
  // Set default result - can't rewind
  Result := False;

  // Inform ui timer that we're about to start rendering video
  TimerInstance.PreRequestRender;

  // Okay to update media position?
  if not OkayToPositionMedia then
  begin
    // We recently updated media position, so give DirectShow a chance to
    // catch up before we make another request. However, we'll return a true
    // result to prevent media manager from delegating the request to the
    // other media stream.
    Result := True;
  end
  else
    with DirectShowInstance do
      if Assigned(DirectShowDVDRenderer) then
      begin
        // Try to skip chapters before we fail over to rewinding
        if PreviousChapter then
          Result := True
        else
        begin
          Position := DirectShowDVDRenderer.Position;
          Position := Position - DEFAULT_BWD_SKIP;
          if Position < 0 then Position := 0;
          Result := DirectShowDVDRenderer.Seek(Position);
        end;
      end;
end;

function TAHMDVDRenderer.NextChapter: Boolean;
begin
  // Set default result - can't change chapter
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      if DirectShowDVDRenderer.ChapterCount > 0 then
      begin
        DirectShowDVDRenderer.NextChapter;
        Result := True;
      end;
end;

function TAHMDVDRenderer.PreviousChapter: Boolean;
begin
  // Set default result - can't change chapter
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      if DirectShowDVDRenderer.ChapterCount > 0 then
      begin
        DirectShowDVDRenderer.PreviousChapter;
        Result := True;
      end;
end;

function TAHMDVDRenderer.MenuActive: Boolean;
begin
  // Set default result - menu inactive
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.MenuActive;
end;

function TAHMDVDRenderer.MenuReturn: Boolean;
begin
  // Set default result - can't navigate to parent
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.MenuExit;
end;

function TAHMDVDRenderer.MenuExit: Boolean;
begin
  // Set default result - can't exit menu
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.MenuReturn;
end;

function TAHMDVDRenderer.MenuSelect: Boolean;
begin
  // Set default result - can't select
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.MenuSelect;
end;

function TAHMDVDRenderer.MenuSelect(X, Y: Single): Boolean;
begin
  // Set default result - can't select
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.MenuSelect(Round(X), Round(Y));
end;

function TAHMDVDRenderer.MenuNavigate(Direction: TAHMDirection): Boolean;
begin
  // Set default result - can't navigate
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.MenuNavigate(Direction);
end;

function TAHMDVDRenderer.MenuNavigate(X, Y: Single): Boolean;
begin
  // Set default result - can't navigate
  Result := False;

  with DirectShowInstance do
    if Assigned(DirectShowDVDRenderer) then
      Result := DirectShowDVDRenderer.MenuNavigate(Round(X), Round(Y));
end;

end.
